package com.yanqu.road.server.manger.activity.yingxin;

import com.yanqu.road.entity.activity.ppl.BallEliminate;
import com.yanqu.road.entity.activity.ppl.PplShootResult;
import com.yanqu.road.entity.activity.yingxin.config.HotPotCollectConfig;
import com.yanqu.road.entity.activity.yingxin.config.YingXinConfig;
import com.yanqu.road.entity.activity.yingxin.game.YingXinGameData;
import com.yanqu.road.entity.activity.yingxin.game.YingXinLine;
import com.yanqu.road.server.protocol.GameErrorCode;
import com.yanqu.road.utils.RandomHelper;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

public class YingXinEliminateLogic {

    // 写死的数据
    private static final int ELIMINATE_NEED_COUNT = 3; // 消除所需数量

    /**
     * 消除球
     */
    public static PplShootResult eliminateBalls(YingXinConfig config, RandomHelper randomHelper,
                                                List<Integer> collectList, YingXinGameData gameData, int multi, int x, int y) {
        PplShootResult result = new PplShootResult();
        List<BallEliminate> eliminateList = new ArrayList<>();
        List<YingXinLine> lines = gameData.getLines();
        int startY = lines.get(0).getY();
        // 游戏结束不能再发射球了
        if (YingXinGameLogic.isGameOver(gameData)) {
            result.setRet(GameErrorCode.E_ACTIVITY_PPL_HAD_GAME_OVER);
            return result;
        }
        // 放置球
        if (startY - y == 1) {
            buildDownEmptyLine(gameData);
            startY = startY - 1;
        }
        if (!canStopOnXy(gameData, x, y)) {
            result.setRet(GameErrorCode.E_ACTIVITY_PPL_POSITION_ERROR);
            return result;
        }
        lines.get(y - startY).getBalls()[x] = gameData.getCur();
        // 统计同颜色数<Y,List<X>>
        Map<Integer, List<Integer>> sameColorMap = new HashMap<>();
        int sameColorCount = countSameColorBalls(lines, x, y, sameColorMap);
        // 消除
        int eliminateMaxY = 0; // 记录消除的最高Y，用于处理掉落
        if (sameColorCount >= ELIMINATE_NEED_COUNT) {
            for (Map.Entry<Integer, List<Integer>> entry : sameColorMap.entrySet()) {
                int tmpY = entry.getKey();
                List<Integer> tmpXList = entry.getValue();
                int[] ballArray = lines.get(tmpY - startY).getBalls();
                for (Integer tmpX : tmpXList) {
                    int hotPotIndex = randomHotPotIndex(config, randomHelper);
                    int count = collectList.get(hotPotIndex);

                    // 加火锅收集进度
                    collectList.set(hotPotIndex, count + getHotPotCollectCount(multi));

                    BallEliminate eliminate = new BallEliminate(tmpX, tmpY, ballArray[tmpX], hotPotIndex);
                    eliminateList.add(eliminate);
                    ballArray[tmpX] = 0;
                }
                eliminateMaxY = eliminateMaxY >= tmpY ? eliminateMaxY : tmpY;
            }
        } else {
            // 没有消除
            return result;
        }
        // 从最顶部开始判断掉落
        boolean borrowTopLine = false;
        if (eliminateMaxY == gameData.getMaxY()) { // 如果消除到最上面一行，先加一行再判断掉落
            borrowTopLine = true;
            YingXinGameLogic.addOneLine(config, gameData, randomHelper);
        }
        int[] ballArray = lines.get(gameData.getMaxY() - startY).getBalls();
        Map<Integer, Set<Integer>> linkedMap = new HashMap<>(); // Map<Y坐标,Set<X坐标>>
        for (int tmpX = 0; tmpX < ballArray.length; tmpX++) {
            if (ballArray[tmpX] != 0) {
                countLinkedBalls(lines, tmpX, gameData.getMaxY(), linkedMap);
            }
        }
        // 未连接的掉落
        for (int i = 0; i < lines.size(); i++) {
            YingXinLine tmpYingXinLine = lines.get(i);
            int[] tmpBallArray = tmpYingXinLine.getBalls();
            for (int tmpX = 0; tmpX < tmpBallArray.length; tmpX++) {
                if (tmpBallArray[tmpX] != 0) {
                    if (!(linkedMap.containsKey(tmpYingXinLine.getY()) && linkedMap.get(tmpYingXinLine.getY()).contains(tmpX))) {
                        int hotPotIndex = randomHotPotIndex(config, randomHelper);
                        int count = collectList.get(hotPotIndex);

                        // 加鼓收集进度
                        collectList.set(hotPotIndex, count + getHotPotCollectCount(multi));

                        BallEliminate eliminate = new BallEliminate(tmpX, tmpYingXinLine.getY(), tmpBallArray[tmpX], hotPotIndex);
                        eliminateList.add(eliminate);
                        tmpBallArray[tmpX] = 0;
                    }
                }
            }
        }
        // 清除空行
        List<YingXinLine> toDeleteLine = new ArrayList<>();
        for (int i = 0; i < lines.size(); i++) {
            if (isLineEmpty(lines.get(i))) {
                toDeleteLine.add(lines.get(i));
            }
        }
        lines.removeAll(toDeleteLine);
        // 清除顶行的球，且不需要新增行时，则借的那行删掉（仅用来判断掉落，不判断消除）
        if (borrowTopLine && lines.size() > YingXinGameLogic.SHOW_LENGTH) {
            lines.remove(lines.size() - 1);
            gameData.setMaxY(lines.get(lines.size() - 1).getY()); // 独立设置新的Y
        }
        // 增加行
        YingXinGameLogic.autoAddLine(config, gameData, randomHelper);

        result.setEliminateList(eliminateList);
        return result;
    }

    /**
     * 统计同颜色数（包括自己）
     *
     * @param sameColorMap 记录同颜色的坐标 Map<Y坐标,List<X坐标>>
     */
    private static int countSameColorBalls(List<YingXinLine> lines, int x, int y,
                                           Map<Integer, List<Integer>> sameColorMap) {
        // 相邻的6个点，根据下标对应
        int[] yArray = {y - 1, y - 1, y, y, y + 1, y + 1};
        int[] xArray;
        if (y % 2 == 0) {
            int[] tmpArray = {x, x + 1, x - 1, x + 1, x, x + 1};
            xArray = tmpArray;
        } else {
            int[] tmpArray = {x - 1, x, x - 1, x + 1, x - 1, x};
            xArray = tmpArray;
        }
        // 把自己加进去
        int sum = 1;
        if (!sameColorMap.containsKey(y)) {
            sameColorMap.put(y, new ArrayList<>());
        }
        sameColorMap.get(y).add(x);
        // 统计
        int startY = lines.get(0).getY();
        int color = lines.get(y - startY).getBalls()[x];
        for (int i = 0; i < xArray.length; i++) {
            if (isXyExist(lines, xArray[i], yArray[i]) && getXyColor(lines, xArray[i], yArray[i]) > 0) { // 坐标合法 && 有颜色
                if (lines.get(yArray[i] - startY).getBalls()[xArray[i]] == color) { // 颜色相同
                    if (!(sameColorMap.containsKey(yArray[i]) && sameColorMap.get(yArray[i]).contains(xArray[i]))) { // 未统计过该点
                        // 递归统计
                        sum += countSameColorBalls(lines, xArray[i], yArray[i], sameColorMap);
                    }
                }
            }
        }
        return sum;
    }

    /**
     * 统计相邻点（包括自己）
     *
     * @param linkedBallMap 存放结果
     */
    private static void countLinkedBalls(List<YingXinLine> lines, int x, int y,
                                         Map<Integer, Set<Integer>> linkedBallMap) {
        // 先把自己加进去
        if (!linkedBallMap.containsKey(y)) {
            linkedBallMap.put(y, new HashSet<>());
        }
        if (!linkedBallMap.get(y).add(x)) {
            return; // 统计过该点了
        }
        // 下方的4个点，根据下标对应
        int[] yArray = {y - 1, y - 1, y, y};
        int[] xArray;
        if (y % 2 == 0) {
            int[] tmp = {x, x + 1, x - 1, x + 1};
            xArray = tmp;
        } else {
            int[] tmp = {x - 1, x, x - 1, x + 1};
            xArray = tmp;
        }
        // 统计
        for (int i = 0; i < xArray.length; i++) {
            if (isXyExist(lines, xArray[i], yArray[i]) && getXyColor(lines, xArray[i], yArray[i]) != 0) {
                countLinkedBalls(lines, xArray[i], yArray[i], linkedBallMap);
            }
        }
    }

    /**
     * 随机取火锅下标
     */
    private static int randomHotPotIndex(YingXinConfig config, RandomHelper randomHelper) {
        List<HotPotCollectConfig> collectList = config.getCollectList();
        List<Integer> weightList = collectList.stream().map(HotPotCollectConfig::getWeight).collect(Collectors.toList());
        return randomHelper.getSafeRandomIndexByWeight(weightList);
    }

    /**
     * 下方新建一空行
     */
    private static void buildDownEmptyLine(YingXinGameData gameData) {
        List<YingXinLine> lines = gameData.getLines();
        int minY = lines.size() > 0 ? lines.get(0).getY() : gameData.getMaxY() + 1;
        YingXinLine pplLine = new YingXinLine();
        pplLine.setBalls(new int[YingXinGameLogic.MAX_BALLS_COUNT]);
        pplLine.setY(minY - 1);
        lines.add(0, pplLine);
    }

    /**
     * 能否停留在该位置
     */
    private static boolean canStopOnXy(YingXinGameData pplUserData, int x, int y) {
        List<YingXinLine> lines = pplUserData.getLines();
        // 判断：该点是否存在、是否已有值
        if (!isXyExist(lines, x, y) || getXyColor(lines, x, y) != 0) {
            return false;
        }
        // 判断：是否有相邻点
        int[] yArray = {y - 1, y - 1, y, y, y + 1, y + 1};
        int[] xArray;
        if (y % 2 == 0) {
            int[] tmpArray = {x, x + 1, x - 1, x + 1, x, x + 1};
            xArray = tmpArray;
        } else {
            int[] tmpArray = {x - 1, x, x - 1, x + 1, x - 1, x};
            xArray = tmpArray;
        }
        for (int i = 0; i < xArray.length; i++) {
            if (isXyExist(lines, xArray[i], yArray[i]) && getXyColor(lines, xArray[i], yArray[i]) != 0) {
                return true;
            }
        }
        return false;
    }

    /**
     * 是否为空行
     */
    private static boolean isLineEmpty(YingXinLine pplLine) {
        int[] ballArray = pplLine.getBalls();
        for (int i = 0; i < ballArray.length; i++) {
            if (ballArray[i] != 0) {
                return false;
            }
        }
        return true;
    }

    /**
     * 坐标是否存在
     */
    private static boolean isXyExist(List<YingXinLine> lines, int x, int y) {
        if (lines == null || lines.size() == 0) {
            return false;
        }
        int startY = lines.get(0).getY();
        int maxY = lines.get(lines.size() - 1).getY();
        if (y < startY || y > maxY) {
            return false;
        }
        YingXinLine pplLine = lines.get(y - startY);
        return x >= 0 && x < pplLine.getBalls().length;
    }

    /**
     * 获取颜色
     */
    private static int getXyColor(List<YingXinLine> lines, int x, int y) {
        int startY = lines.get(0).getY();
        int[] ballArray = lines.get(y - startY).getBalls();
        return ballArray[x];
    }


    // =============== 多倍 ================

    /**
     * 获取一个球火锅加的收集数
     */
    private static int getHotPotCollectCount(int multi) {
        return multi;
    }

}
